package com.yang.ddd.application.service.impl;


import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yang.ddd.shared.auth.SpaceUserAuthManager;
import com.yang.ddd.application.service.SpaceApplicationService;
import com.yang.ddd.application.service.SpaceUserApplicationService;
import com.yang.ddd.application.service.UserApplicationService;
import com.yang.ddd.domain.space.entity.Space;
import com.yang.ddd.domain.space.entity.SpaceLevel;
import com.yang.ddd.domain.space.entity.SpaceUser;
import com.yang.ddd.domain.space.service.SpaceDomainService;
import com.yang.ddd.domain.space.valueobject.SpaceLevelEnum;
import com.yang.ddd.domain.space.valueobject.SpaceRoleEnum;
import com.yang.ddd.domain.space.valueobject.SpaceTypeEnum;
import com.yang.ddd.domain.user.entity.User;
import com.yang.ddd.infrastructure.common.DeleteRequest;
import com.yang.ddd.infrastructure.common.ErrorCode;
import com.yang.ddd.infrastructure.exception.BusinessException;
import com.yang.ddd.infrastructure.exception.ThrowUtils;
import com.yang.ddd.interfaces.dto.space.SpaceAddRequest;
import com.yang.ddd.interfaces.dto.space.SpaceEditRequest;
import com.yang.ddd.interfaces.dto.space.SpaceQueryRequest;
import com.yang.ddd.interfaces.dto.space.SpaceUpdateRequest;
import com.yang.ddd.interfaces.vo.space.SpaceVO;
import com.yang.ddd.interfaces.vo.user.UserVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.Set;

/**
 * @author Young
 * @description 针对表【picture(图片)】的数据库操作Service实现
 * @createDate 2025-03-25 23:25:31
 */
@Service
@Slf4j
public class SpaceApplicationServiceImpl implements SpaceApplicationService {

    @Resource
    private SpaceDomainService spaceDomainService;

    @Resource
    private TransactionTemplate transactionTemplate;

    @Resource
    private UserApplicationService userApplicationService;

    @Resource
    private SpaceUserApplicationService spaceUserApplicationService;

    @Resource
    private SpaceUserAuthManager spaceUserAuthManager;

//    @Resource
//    @Lazy
//    private DynamicShardingManager dynamicShardingManager;


    @Override
    public long addSpace(SpaceAddRequest spaceAddRequest, User loginUser) {
        // 在此处将实体类和 DTO 进行转换
        Space space = new Space();
        BeanUtils.copyProperties(spaceAddRequest, space);
        // 默认值
        if (StrUtil.isBlank(spaceAddRequest.getSpaceName())) {
            space.setSpaceName("默认空间");
        }
        if (spaceAddRequest.getSpaceLevel() == null) {
            space.setSpaceLevel(SpaceLevelEnum.COMMON.getValue());
        }
        if (spaceAddRequest.getSpaceType() == null) {
            spaceAddRequest.setSpaceType(SpaceTypeEnum.PRIVATE.getValue());
        }
        // 填充数据
        this.fillSpaceBySpaceLevel(space);
        // 数据校验
        this.validSpace(space, true);
        Long userId = loginUser.getId();
        space.setUserId(userId);
        // 权限校验
        if (SpaceLevelEnum.COMMON.getValue() != space.getSpaceLevel() && !User.isAdmin(loginUser)) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "无权限创建指定级别的空间");
        }
        // 针对用户进行加锁（减小锁的粒度）
        String lock = String.valueOf(userId).intern();
        synchronized (lock) {
            Long newSpaceId = transactionTemplate.execute(status -> {
                // 同一个用户只能创建一个私有空间，以及一个团队空间，也就是一个用户最多拥有两个空间（且是不同类型的空间）
                if (!User.isAdmin(loginUser)) {
                    boolean exists =  spaceDomainService.isExists(userId, spaceAddRequest.getSpaceType());
                    ThrowUtils.throwIf(exists, ErrorCode.OPERATION_ERROR, "每个用户每类空间仅能创建一个");
                }
                // 写入数据库
                boolean result = spaceDomainService.save(space);
                ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
                // 如果是团队空间，关联新增团队成员记录
                if (SpaceTypeEnum.TEAM.getValue() == spaceAddRequest.getSpaceType()) {
                    SpaceUser spaceUser = new SpaceUser();
                    spaceUser.setSpaceId(space.getId());
                    spaceUser.setUserId(userId);
                    spaceUser.setSpaceRole(SpaceRoleEnum.ADMIN.getValue());
                    result = spaceUserApplicationService.save(spaceUser);
                    ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR, "创建团队成员记录失败");
                }

                // 创建分表（仅生效旗舰空间）
//                dynamicShardingManager.createSpacePictureTable(space);
                // 返回新写入的数据 id
                return space.getId();
            });
            // 返回结果是包装类，可以做一些处理
            return Optional.ofNullable(newSpaceId).orElse(-1L);
        }
    }

    /**
     * 根据级别自动填充space空间大小
     *
     * @param space
     */
    @Override
    public void fillSpaceBySpaceLevel(Space space) {
        // 根据空间级别，自动填充限额
        SpaceLevelEnum spaceLevelEnum = SpaceLevelEnum.getEnumByValue(space.getSpaceLevel());
        if (spaceLevelEnum != null) {
            long maxSize = spaceLevelEnum.getMaxSize();
            if (space.getMaxSize() == null) {
                space.setMaxSize(maxSize);
            }
            long maxCount = spaceLevelEnum.getMaxCount();
            if (space.getMaxCount() == null) {
                space.setMaxCount(maxCount);
            }
        }
    }


    /**
     * 校验数
     *
     * @param space
     * @param add   区分是添加操作还是更新操作
     */
    @Override
    public void validSpace(Space space, boolean add) {
        Space.validSpace(space, add);
    }


    @Override
    public SpaceVO getSpacecVO(Space space, HttpServletRequest request) {
        User byId = userApplicationService.getUserById(space.getUserId());
        UserVO userVO = userApplicationService.getUserVO(byId);
        SpaceVO spaceVO = SpaceVO.objToVo(space);
        spaceVO.setUser(userVO);
        return spaceVO;
    }


    @Override
    public Wrapper<Space> getQueryWrapper(SpaceQueryRequest spaceQueryRequest) {
        return spaceDomainService.getQueryWrapper(spaceQueryRequest);
    }

    @Override
    public void checkSpaceAuth(User loginUser, Space space) {
        if (!space.getUserId().equals(loginUser.getId()) && !User.isAdmin(loginUser)) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "没有空间访问权限");
        }
    }

    @Override
    public Space getById(Long spaceId) {
        return spaceDomainService.getById(spaceId);
    }

    @Override
    public List<Space> list(QueryWrapper<Space> queryWrapper) {
        return spaceDomainService.list(queryWrapper);
    }

    @Override
    public List<Space> listByIds(Set<Long> spaceIdSet) {
        return spaceDomainService.listByIds(spaceIdSet);
    }

    /**
     *
     * @param finalSpaceId 空间id
     * @param picSize  更新大小
     * @param b true：消耗额度空间 false：增加额度空间
     * @return
     */
    @Override
    public boolean updateQuota(Long finalSpaceId, Long picSize, boolean b) {
        return spaceDomainService.updateQuota(finalSpaceId,  picSize, b);
    }

    @Override
    public Page<Space> listSpaceByPage(SpaceQueryRequest spaceQueryRequest) {
        return spaceDomainService.listSpaceByPage(spaceQueryRequest);
    }

    @Override
    public Page<SpaceVO> listSpaceVOByPage(SpaceQueryRequest spaceQueryRequest) {
        return spaceDomainService.listSpaceVOByPage(spaceQueryRequest);
    }

    @Override
    public List<SpaceLevel> listSpaceLevel() {
        return spaceDomainService.listSpaceLevel();
    }

    @Override
    public SpaceVO getSpaceVOById(long id, HttpServletRequest request) {
        ThrowUtils.throwIf(id <= 0, ErrorCode.PARAMS_ERROR);
        // 查询数据库
        Space space = this.getById(id);
        ThrowUtils.throwIf(space == null, ErrorCode.NOT_FOUND_ERROR);
        User loginUser = userApplicationService.getLoginUser(request);
        SpaceVO spaceVO = this.getSpacecVO(space, request);
        List<String> permissionList = spaceUserAuthManager.getPermissionList(space, loginUser);
        spaceVO.setPermissionList(permissionList);
        return spaceVO;
    }

    @Override
    public Boolean deleteSpace(DeleteRequest deleteRequest, HttpServletRequest request) {
        if (deleteRequest == null || deleteRequest.getId() <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        User loginUser = userApplicationService.getLoginUser(request);
        long id = deleteRequest.getId();
        // 判断是否存在
        Space oldSpace = this.getById(id);
        ThrowUtils.throwIf(oldSpace == null, ErrorCode.NOT_FOUND_ERROR);
        // 仅本人或管理员可删除
        if (!oldSpace.getUserId().equals(loginUser.getId()) && !User.isAdmin(loginUser)) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
        }
        // 操作数据库
        return spaceDomainService.removeById(id);
    }

    @Override
    public void editSpace(SpaceEditRequest spaceEditRequest, HttpServletRequest request) {
        if (spaceEditRequest == null || spaceEditRequest.getId() <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        // 在此处将实体类和 DTO 进行转换
        Space space = new Space();
        BeanUtils.copyProperties(spaceEditRequest, space);
        // 设置编辑时间
        space.setEditTime(new Date());
        // 数据校验
        this.validSpace(space, false);
        User loginUser = userApplicationService.getLoginUser(request);
        // 判断是否存在
        Space oldSpace = this.getById(spaceEditRequest.getId());
        ThrowUtils.throwIf(oldSpace == null, ErrorCode.NOT_FOUND_ERROR);
        // 仅本人或管理员可编辑
        if (!oldSpace.getUserId().equals(loginUser.getId()) && !User.isAdmin(loginUser)) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
        }
        // 操作数据库
        boolean result = spaceDomainService.updateById(space);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
    }

    @Override
    public void updateSpace(SpaceUpdateRequest spaceUpdateRequest) {
        if (spaceUpdateRequest == null || spaceUpdateRequest.getId() <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        // 将实体类和 DTO 进行转换
        Space space = new Space();
        BeanUtils.copyProperties(spaceUpdateRequest, space);
        // 自动填充数据
        this.fillSpaceBySpaceLevel(space);
        // 数据校验
        this.validSpace(space, false);
        // 判断是否存在
        long id = spaceUpdateRequest.getId();
        Space oldSpace = this.getById(id);
        ThrowUtils.throwIf(oldSpace == null, ErrorCode.NOT_FOUND_ERROR);
        // 操作数据库
        boolean result = spaceDomainService.updateById(space);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
    }

}







